#pragma once

#include "base_win.h"
#include <Windows.h>

typedef int Atomic32;
typedef intptr_t AtomicWord;

inline Atomic32 NoBarrier_CompareAndSwap(
	volatile Atomic32* ptr,
	Atomic32 old_value,
	Atomic32 new_value
	)
{
	LONG result = InterlockedCompareExchange(
		reinterpret_cast<volatile LONG*>(ptr),
		static_cast<LONG>(new_value),
		static_cast<LONG>(old_value));
	return static_cast<Atomic32>(result);
}

inline Atomic32 NoBarrier_AtomicExchange(
	volatile Atomic32* ptr,
	Atomic32 new_value
	)
{
	LONG result = InterlockedExchange(
		reinterpret_cast<volatile LONG*>(ptr),
		static_cast<LONG>(new_value));
	return static_cast<Atomic32>(result);
}

inline Atomic32 Barrier_AtomicIncrement(
	volatile Atomic32* ptr,
	Atomic32 increment
	)
{
	return InterlockedExchangeAdd(
		reinterpret_cast<volatile LONG*>(ptr),
		static_cast<LONG>(increment)) + increment;
}

inline Atomic32 NoBarrier_AtomicIncrement(
	volatile Atomic32* ptr,
	Atomic32 increment
	)
{
	return Barrier_AtomicIncrement(ptr, increment);
}

/*
inline void MemoryBarrier()
{
	// We use MemoryBarrier from WinNT.h
	::MemoryBarrier();
}
*/

inline Atomic32 Acquire_CompareAndSwap(
	volatile Atomic32* ptr,
	Atomic32 old_value,
	Atomic32 new_value
	)
{
	return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}

inline Atomic32 Release_CompareAndSwap(
	volatile Atomic32* ptr,
	Atomic32 old_value,
	Atomic32 new_value
	)
{
	return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}

inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value)
{
	*ptr = value;
}

inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value)
{
	NoBarrier_AtomicExchange(ptr, value);
	// acts as a barrier in this implementation
}

inline void Release_Store(volatile Atomic32* ptr, Atomic32 value)
{
	*ptr = value; // works w/o barrier for current Intel chips as of June 2005
	// See comments in Atomic64 version of Release_Store() below.
}

inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr)
{
	return *ptr;
}

inline Atomic32 Acquire_Load(volatile const Atomic32* ptr)
{
	Atomic32 value = *ptr;
	return value;
}

inline Atomic32 Release_Load(volatile const Atomic32* ptr)
{
	MemoryBarrier();
	return *ptr;
}

typedef Atomic32 AtomicRefCount;

inline void AtomicRefCountIncN(
	volatile AtomicRefCount *ptr,
	AtomicRefCount increment
	)
{
	NoBarrier_AtomicIncrement(ptr, increment);
}

inline bool AtomicRefCountDecN(
	volatile AtomicRefCount *ptr,
	AtomicRefCount decrement
	)
{
	bool res = (Barrier_AtomicIncrement(ptr, -decrement) != 0);
	return res;
}

inline void AtomicRefCountInc(volatile AtomicRefCount *ptr)
{
	AtomicRefCountIncN(ptr, 1);
}

inline bool AtomicRefCountDec(volatile AtomicRefCount *ptr)
{
	return AtomicRefCountDecN(ptr, 1);
}

inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr)
{
	bool res = (Acquire_Load(ptr) == 1);
	return res;
}

inline bool AtomicRefCountIsZero(volatile AtomicRefCount *ptr)
{
	bool res = (Acquire_Load(ptr) == 0);
	return res;
}
